<?php
declare(strict_types=1);

namespace App\Helper;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use Hyperf\Guzzle\CoroutineHandler;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use Hyperf\Support\Traits\StaticInstance;
use Psr\Http\Message\ResponseInterface;

class HttpHelper
{

    use StaticInstance;

    protected bool $debug;
    protected Client $client;

    public function __construct()
    {
        $this->debug = !IS_PROD;
        $this->client = new Client([
            'handler' => HandlerStack::create(new CoroutineHandler),
            'timeout' => 3,
            // 关闭ssl证书验证
            'verify' => false,
            'swoole' => [
                'timeout' => 10, 'socket_buffer_size' => 1024 * 1024 * 2,
            ],
        ]);
    }

    /**
     * @param string $url
     * @param array $queries
     * @param array $headers
     * @return null[]
     */
    public function get(string $url, array $queries = [], array $headers = []): array
    {
        return $this->request(
            new Request('GET', $url, $headers),
            [
                RequestOptions::QUERY => $queries
            ]
        );
    }

    /**
     * @param string $url
     * @param array $params
     * @param bool $json
     * @param array $headers
     * @return null[]
     */
    public function put(string $url, array $params = [], bool $json = true, array $headers = []): array
    {
        $bodyType = $json ? RequestOptions::BODY : RequestOptions::FORM_PARAMS;
        if ($json) {
            $headers['Content-Type'] = 'application/json';
            $params = json_encode($params, JSON_UNESCAPED_UNICODE);
        }
        return $this->request(
            new Request('PUT', $url, $headers),
            [
                $bodyType => $params,
            ]
        );
    }

    /**
     * @param string $url
     * @param array $params
     * @param bool $json
     * @param array $headers
     * @return null[]
     */
    public function post(string $url, array $params = [], bool $json = true, array $headers = []): array
    {
        $bodyType = $json ? RequestOptions::BODY : RequestOptions::FORM_PARAMS;
        if ($json) {
            $headers['Content-Type'] = 'application/json';
            $params = json_encode($params, JSON_UNESCAPED_UNICODE);
        }
        return $this->request(
            new Request('POST', $url, $headers),
            [
                $bodyType => $params,
            ]
        );
    }

    /**
     * @param string $url
     * @param array $queries
     * @param array $headers
     * @return null[]
     */
    public function delete(string $url, array $queries = [], array $headers = []): array
    {
        return $this->request(
            new Request('DELETE', $url, $headers),
            [
                RequestOptions::QUERY => $queries
            ]
        );
    }

    /**
     * 发起请求
     * @param Request $request
     * @param array $options
     * @return null[]
     */
    protected function request(Request $request, array $options = []): array
    {
        $resp = ['code' => null, 'message' => 'Success', 'body' => null, 'json' => null, 'type' => 'text', 'ext' => null, 'content-type' => null];
        if ($this->debug) {
            stdPrinter()->info("HttpHelper [" . $request->getMethod() . "]：" . $request->getUri());
            stdPrinter()->info("Request Option : \n" . var_export($options, true));
        }
        try {
            $response = $this->client->send($request, $options);
            $this->parseContent($response, $resp);
        } catch (ClientException|ServerException $e) {
            $resp['message'] = $e->getMessage();
            $this->parseContent($e->getResponse(), $resp);
        } catch (GuzzleException $e) {
            $resp['code'] = 502;
            $resp['message'] = $e->getMessage();
        }
        return $resp;
    }

    /**
     * 从响应中解析出内容
     * @param ResponseInterface $response
     * @param $resp
     * @return void
     */
    protected function parseContent(ResponseInterface $response, &$resp): void
    {
        $resp['code'] = $response->getStatusCode();
        $resp['body'] = $response->getBody()->getContents();
        $contentType = $response->getHeader('Content-Type')[0];
        $contentType = explode(';', $contentType)[0];
        $resp['content-type'] = $contentType;
        $resp['json'] = @json_decode($resp['body'], true);
        if (str_starts_with($contentType, 'application/json') || $resp['json']) {
            $resp['type'] = 'json';
        } elseif (!str_starts_with($contentType, 'text/')) {
            $resp['type'] = 'buffer';
            $resp['ext'] = explode('/', $contentType)[1];
        }
    }

}